<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>在线图片编辑工具</title>
</head>
<style>
  @import url("css/jquery-ui-1.8.16.custom.css");
  @import url("css/webgl-tutorials.css");
  @import url("css/cropper.css");
  @import url("css/index.css");
</style>
<body>
  <div class="nav_bar" >
    <div class="buttons">
      <p id="selectFile">选择图片</p>
    </div>
    <div class="buttons">
      <p  id="corpper" >裁剪</p>
      <p  id="confirm" >确定</p>
      <p  id="cance" >取消</p>
    </div>
    <div class="buttons">
      <p  id="picturex" data="">左右镜像</p>
      <p  id="picturey" data="y">上下镜像</p>
    </div>
    <div class="buttons">
      <p  id="scale" >缩放</p>
      <p  id="rotate" >旋转</p>
    </div>
    <div class="buttons">
      <p  id="setColor" >调色</p>
      <p id="filter">
        <label for="格式">滤镜</label>
        <select id="selectFilter"  >
          <!-- 卷积滤镜选项 -->
        </select>
      </p>
    </div>
    <div class="buttons">
      <p  id="save" data="">保存</p>
      <p>
        <label for="格式">格式</label>
        <select  id="selectFormat" >
          <option value="0" selected>jpeg</option>
          <option value="1">webp</option>
          <option value="2">png</option>
        </select>
      </p>
      <p>
        <label for="格式">质量</label>
      </p>
      <div id="quality"></div>
    </div>

    <div class="slideBar">
      <div id="缩放" style="display: none;"></div>
    </div>
  </div>
  <div class="GLLayer">
    <div class="GLContainer" id="scaleLayer"  >
      <canvas id="canvas"></canvas>
    </div>
  </div>
  <input id="file" type="file"  accept=".jpg, .jpeg, .png" >
  <div id="uiContainer" style="display: none;" >
    <div id="ui">
      <div id="r"></div>
      <div id="g"></div>
      <div id="b"></div>
      <div id="a"></div>
      <div id="亮度"></div>
      <div id="饱和度">饱和度</div>
      <div id="对比度">对比度</div>
    </div>
  </div>
  <div class="scaleTool" id="scaleTool">
    <div id="scaleRange">
      <p style="font-weight: bolder;" id="reduce">-</p>
      <p id="scaleValue">100</p><i>%</i>
      <p style="font-weight: bolder;" id="add">+</p>
    </div>
  </div>
</body>
<!-- vertex shader -->
<script id="vertex-shader-2d" type="x-shader/x-vertex">
  attribute vec2 a_position;
  attribute vec2 a_texCoord;
  varying vec2 v_texCoord;
  uniform mat3 u_matrix;
  void main(){
    vec2 position = (u_matrix * vec3(a_position, 1)).xy;
    gl_Position = vec4(position, 0, 1);
    //将texCoord传递到片段着色器
    //GPU将在点之间对该值进行插值。
    v_texCoord = a_texCoord;
  }
</script>
<!-- fragment shader -->
<script id="fragment-shader-2d" type="x-shader/x-fragment">
  precision mediump float;

  uniform sampler2D u_image;
  varying vec2 v_texCoord;
  uniform vec4 u_rgba;
  uniform vec2 u_textureSize;
  uniform float u_kernel[9];
  uniform float u_kernelWeight;
  uniform float u_light;
  uniform float u_saturation;
  uniform float u_contrastRatio;
  
  uniform int u_filter;
  void main (){
    // 计算1像素对应的纹理坐标
    vec2 onePixel = vec2(1.0, 1.0) / u_textureSize*1.0;
    vec4 color = texture2D(u_image, v_texCoord); 
    // 每个像素进行卷积计算
    vec4 colorSum =
      texture2D(u_image, v_texCoord + onePixel * vec2(-1, -1))*u_rgba * u_kernel[0] +
      texture2D(u_image, v_texCoord + onePixel * vec2( 0, -1))*u_rgba * u_kernel[1] +
      texture2D(u_image, v_texCoord + onePixel * vec2( 1, -1))*u_rgba * u_kernel[2] +
      texture2D(u_image, v_texCoord + onePixel * vec2(-1,  0))*u_rgba * u_kernel[3] +
      texture2D(u_image, v_texCoord + onePixel * vec2( 0,  0))*u_rgba * u_kernel[4] +
      texture2D(u_image, v_texCoord + onePixel * vec2( 1,  0))*u_rgba * u_kernel[5] +
      texture2D(u_image, v_texCoord + onePixel * vec2(-1,  1))*u_rgba * u_kernel[6] +
      texture2D(u_image, v_texCoord + onePixel * vec2( 0,  1))*u_rgba * u_kernel[7] +
      texture2D(u_image, v_texCoord + onePixel * vec2( 1,  1))*u_rgba * u_kernel[8] ;

    color = vec4((colorSum / u_kernelWeight).rgb, u_rgba.a);

    vec3 avg = vec3(0.5,0.5,0.5);                
    vec3 luncoeff = vec3 (0.2125, 0.7154, 0.0721);                
    //计算亮度                                                    
    vec3 brtColor = color.rgb * u_light;                                  
    float intensityf = dot(brtColor,luncoeff);                         
    vec3 intensity = vec3(intensityf,intensityf,intensityf);        
    //计算饱和度        
    vec3 satColor = vec3(mix(intensity, brtColor, u_saturation));                
    //计算对比度    
    vec3 conColor = vec3(mix(avg, satColor, u_contrastRatio));                    
    gl_FragColor = vec4(conColor.r,conColor.g,conColor.b,color.a);    
    
  }
</script>

<script src="js/webgl-utils.js" ></script>
<script src="js/webgl-lessons-ui.js" ></script>
<script src="js/gl-matrix.js"></script>
<script src="js/cropper.js"></script>
<script src="js/filters.js"></script>
<script>
  
  function isMobileDevice() {
    const userAgent = navigator.userAgent.toLowerCase();
    const mobileKeywords = ['android', 'ios', 'iphone', 'ipad', 'ipod', 'windows phone'];
    
    for (let keyword of mobileKeywords) {
      if (userAgent.indexOf(keyword) > -1) {
        return true;
      }
    }
    return false;
  }

  var isMobileDevice = isMobileDevice()

</script>
<!-- cropper -->
<script>
  var cropper = null;
  var convas = document.querySelector('canvas')
  var cropCanvas = null ;
  // 选择本地图像
  {
    var selectFile = document.getElementById('selectFile')
    selectFile.addEventListener('click',function(){
      if(!cropper){
       domFile.click()
      }
    })
  }
  // 保存图像
  {
    var saveBtn = document.querySelector('#save')
    var selectFormat = document.querySelector('#selectFormat')
    const types = [
      'image/jpeg',
      'image/webp',
      'image/png',
    ]
    var formatIndex = 0;
    var encoderOptions = 0.95;

    saveBtn.addEventListener('click',function(){
      render(image)
      canvas.toBlob(function(blob){
        var a = document.createElement('a')
        document.body.appendChild(a);
        a.style.display = "block";
        a.download = `${canvas.width}+${canvas.height}` + `.${types[formatIndex].replace('image/','')}`;
        a.href = window.URL.createObjectURL(blob);
        a.click()
      },types[formatIndex],encoderOptions)
    },false)

    selectFormat.addEventListener('change',function(e){
      console.log(e.target.value)
      formatIndex = Number(e.target.value)
    })
  }
  // 上下↕ 左右↔ 镜像
  {
    var picture = [1,1];
    var picturexBtn = document.querySelector('#picturex')
    var pictureyBtn = document.querySelector('#picturey')
    function pictureFun(index){
      return function(){
        picture[index] = - picture[index]
        render(image)
      }
    }
    picturexBtn.addEventListener("click",pictureFun(0))
    pictureyBtn.addEventListener("click",pictureFun(1))
  }
  // 缩放 旋转 
  {
    var scaleBtn = document.querySelector('#scale')
    var rotateBtn = document.querySelector('#rotate')
    rotateBtn.addEventListener('click',function(e){
      if(angle>270){
        angle = 0
      }
      angle+=90
      angleInRadians = angle * Math.PI / 180;
      render(image)
    })

    scaleBtn.addEventListener('click',function(){
      showItem(['uiContainer'])
      if(document.getElementById('缩放').style.display !='none'){
        document.getElementById('缩放').style.display = 'none'
      }else{
        document.getElementById('缩放').style.display = 'block'
      }
    })
  }
  // 调色
  {
    var setColorBtn = document.querySelector('#setColor')

    setColorBtn.addEventListener('click',function(e){
      showItem(['缩放'])
      if(document.getElementById('uiContainer').style.display !='none'){
        document.getElementById('uiContainer').style.display = 'none'
      }else{
        document.getElementById('uiContainer').style.display = 'block'
      }
    })
  }
  // 滤镜
  {
    var selectFilterBtn = document.querySelector('#selectFilter')
    initFilterSelect(selectFilterBtn)
    selectFilterBtn.addEventListener('change',function(e){
      filterName = e.target.value
      render(image)
    })
  }
  // 隐藏所以 显示当前
  function showItem(doms){
    doms.forEach(ele => {
      document.getElementById(`${ele}`).style.display = 'none' 
    });
  }
  
  var corpperBtn = document.querySelector('#corpper')
  var confirmBtn = document.querySelector('#confirm')
  var canceBtn = document.querySelector('#cance')
  // 裁剪配置
  var options = {
    scaleNum:1,  //裁剪框容器缩放值
    movable:false, //是否可以移动图片。
    scalable:false, //是否可以缩放图片（以图片中心点为原点进行缩放）。
    zoomOnWheel:false, //是否允许通过鼠标滚轮缩放图片。
    ready: function (e) {
      console.log(cropper.getCroppedCanvas())
      cropCanvas = cropper.getCroppedCanvas()
    },
    cropend: function (e) {
      console.log(cropper.getCroppedCanvas())
      cropCanvas = cropper.getCroppedCanvas()
    },
    zoom: function (e) {
      console.log(e,'zoom');
    }
    
  }
  // 裁剪
  corpperBtn.addEventListener('click',function(e){
    // 这里要再次渲染次
    render(image)
    cropper = new Cropper(convas, options);
  })
  // 确认裁剪
  confirmBtn.addEventListener('click',function(e){
    if(!cropCanvas){
      return 
    }
    image.src = cropCanvas.toDataURL('image/png', 1);
    cropper.destroy()
    cropper = null
  })
  // 销毁 取消
  canceBtn.addEventListener('click',function(e){
    cropper.destroy()
    cropper = null
  })
  
</script>
<script>
  console.log(isMobileDevice)
  // 滤镜名称
  var filterName = '正常'
  var image = new Image();
  image.src = 'img/你的名字.jpg'
  var fileList = []

  image.onload = function(){
    // pc端设置
    if(image.width>document.body.clientWidth||image.height>document.body.clientHeight){
      var arr = [image.width/document.body.clientWidth,image.height/document.body.clientHeight]
      if(arr[1]>arr[0]){
        var scaleing = document.body.clientHeight*.85/image.height
      }else{
        var scaleing = document.body.clientWidth*.85/image.width
      }
      scaleValue.innerHTML=(scaleing*100).toFixed(0)
    }
    console.log(scaleValue.innerHTML)
    setCanvasScale() //加载图片后设置视图区域缩放
    render(image)
  }
  
  var domFile = document.getElementById('file')
  var scaleToolFile = document.getElementById('scaleTool')

  domFile.addEventListener("change",function(event){
    // 初始化全局变量
    initVariable()
    fileList = event.target.files
    if(fileList.length>0){
      domFile.style.display= "none"
      scaleToolFile.style.display = "block"
      readFile(fileList[0])
    }else{
      domFile.style.display= "block"
      scaleToolFile.style.display = "block"
    }
  })
  
  // 缩放视图工具栏操作
  var reduceDom = document.getElementById('reduce')
  var addDom = document.getElementById('add')
  var scaleDom = document.getElementById('scaleValue')
  scaleValue.innerHTML = 100
  reduceDom.addEventListener("click",function(event){
    setCanvasScale(-10)
  })
  addDom.addEventListener("click",function(event){
    setCanvasScale(10)
  })
  scaleLayer.addEventListener("wheel",(e)=>{
    if(!image.src){
      return 
    }
    e.preventDefault()
    if(e.deltaY>0){
      setCanvasScale(-3)
    }else{
      setCanvasScale(3)
    }
  },{passive:false})

  function setCanvasScale(value=0,translate={x:0,y:0}){
    if((Number(scaleDom.innerHTML)+value)>10){
      scaleDom.innerHTML = (Number(scaleDom.innerHTML)+value).toFixed(0)
    }else{
      scaleDom.innerHTML = 10
    }
    if(scaleDom.innerHTML<10){
      return 
    }
    // 更改裁剪器的 缩放比
    options.scaleNum = Number(scaleValue.innerHTML/100)

    var value = (scaleDom.innerHTML/100).toFixed(2)
    scaleLayer.style.transform = `translate(-50%, -50%) scale(${Number(value)})`
  }
  function readFile(file){
    var reader = new FileReader()
    reader.onload=function(){
      image.src = reader.result;
    }
    reader.readAsDataURL(file);
  }
  // 初始化全局变量
  function initVariable(){
    rgba = [1.0,1.0,1.0,1.0]
    scale = 1
    light = 1.0
    saturation = 1.0
    contrastRatio = 1.0
    angleInRadians = 0 //弧度
    angle = 0 //角度
    filterName = '正常'
    encoderOptions = 0.95; //保存的图像质量
    webglLessonsUI.setupSlider("#缩放", {value: scale, slide: updateScale, min: 0, step: 0.01, max: 2,precision: 2});
    webglLessonsUI.setupSlider("#quality", {value: encoderOptions, slide: updateQuality, min: .5, step: 0.01, max: 1,precision: 2});
    // webglLessonsUI.setupSlider("#旋转", {value: angle, slide: updateAngle, min: 0, step: 90, max: 360,precision: 1});
    webglLessonsUI.setupSlider("#r", {value: rgba[0], slide: updateCameraRange(0), min: 0, step: 0.01, max: 2,precision: 2});
    webglLessonsUI.setupSlider("#g", {value: rgba[1], slide: updateCameraRange(1), min: 0, step: 0.01, max: 2,precision: 2});
    webglLessonsUI.setupSlider("#b", {value: rgba[2], slide: updateCameraRange(2), min: 0, step: 0.01, max: 5,precision: 2});
    webglLessonsUI.setupSlider("#a", {value: rgba[3], slide: updateCameraRange(3), min: 0, step: 0.01, max: 1,precision: 2});
    webglLessonsUI.setupSlider("#亮度", {value: light, slide: updateLight, min: 0.5, step: 0.1, max: 3,precision: 1});
    webglLessonsUI.setupSlider("#饱和度", {value: saturation, slide: updateSaturation, min: 0.0, step: 0.1, max: 3,precision: 1});
    webglLessonsUI.setupSlider("#对比度", {value: contrastRatio, slide: updateContrastRatio, min: 0.5, step: 0.1, max: 3,precision: 1});
  }
  // 工具项
  var rgba = [1.0,1.0,1.0,1.0]
  var scale = 1
  var light = 1.0
  var saturation = 1.0
  var contrastRatio = 1.0
  var angleInRadians = 0 //弧度
  var angle = 0 //角度
  webglLessonsUI.setupSlider("#缩放", {value: scale, slide: updateScale, min: 0, step: 0.01, max: 2,precision: 2});
  webglLessonsUI.setupSlider("#quality", {value: encoderOptions, slide: updateQuality, min: .5, step: 0.01, max: 1,precision: 2});
  // webglLessonsUI.setupSlider("#旋转", {value: angle, slide: updateAngle, min: 0, step: 90, max: 360,precision: 1});
  webglLessonsUI.setupSlider("#r", {value: rgba[0], slide: updateCameraRange(0), min: 0, step: 0.01, max: 2,precision: 2});
  webglLessonsUI.setupSlider("#g", {value: rgba[1], slide: updateCameraRange(1), min: 0, step: 0.01, max: 2,precision: 2});
  webglLessonsUI.setupSlider("#b", {value: rgba[2], slide: updateCameraRange(2), min: 0, step: 0.01, max: 5,precision: 2});
  webglLessonsUI.setupSlider("#a", {value: rgba[3], slide: updateCameraRange(3), min: 0, step: 0.01, max: 1,precision: 2});
  webglLessonsUI.setupSlider("#亮度", {value: light, slide: updateLight, min: 0.5, step: 0.1, max: 3,precision: 1});
  webglLessonsUI.setupSlider("#饱和度", {value: saturation, slide: updateSaturation, min: 0.0, step: 0.1, max: 3,precision: 1});
  webglLessonsUI.setupSlider("#对比度", {value: contrastRatio, slide: updateContrastRatio, min: 0.5, step: 0.1, max: 3,precision: 1});
  
  function updateScale(event, ui) {
    scale = ui.value;
    render(image)
  }
  function updateQuality(event, ui) {
    encoderOptions = ui.value;
  }
  function updateAngle(event, ui) {
    angle = ui.value;
    var angleInDegrees = 360 - ui.value;
    angleInRadians = angleInDegrees * Math.PI / 180;
    render(image)
  }
  function updateLight(event, ui) {
    light = ui.value;
    render(image)
  }
  function updateSaturation(event, ui) {
    saturation = ui.value;
    render(image)
  }
  function updateContrastRatio(event, ui) {
    contrastRatio = ui.value;
    render(image)
  }
  function updateCameraRange(index){
    return function (event, ui) {
      rgba[index] = ui.value;
      render(image)
    }
  }
  // 矩阵运算
  // 所有改变图像本质的操作在这计算
  function getMmatrix(width,height,mat3){
    var matrix_3 = glMatrix.mat3.create();
    glMatrix.mat3.projection(matrix_3,width,height)
    glMatrix.mat3.translate(matrix_3,matrix_3,[width/2,height/2,0]);
    glMatrix.mat3.rotate(matrix_3,matrix_3,angleInRadians);
    // 镜像操作 picture xy轴 镜像
    glMatrix.mat3.scale(matrix_3,matrix_3,picture);
    if(mat3){
      var mat3Center = glMatrix.mat3.create();
      var mat3C = glMatrix.mat3.translate(mat3Center,mat3Center,[...mat3,0]);
      glMatrix.mat3.multiply(matrix_3,matrix_3,mat3C)
    }
    return matrix_3
  }
  // 卷积计算
  function computeKernelWeight(kernel) {
    var weight = kernel.reduce(function(prev, curr) {
        return prev + curr;
    });
    return weight <= 0 ? 1 : weight;
  }


  function render(image){
    
    /** @type {HTMLCanvasElement} */
    if(angle==90||angle==270){
      canvas.style.width = image.height*scale+'px'
      canvas.style.height = image.width*scale+'px'
    }else{
      canvas.style.width = image.width*scale+'px'
      canvas.style.height = image.height*scale+'px'
    }
    
    var gl = canvas.getContext("webgl")
    if(!gl){
      return;
    }
    // console.log(image.width*scale,image.height*scale)

    // 创建着色器程序
    var program = webglUtils.createProgramFromScripts(gl,["vertex-shader-2d","fragment-shader-2d"]);

    // 查找顶点数据需要放在哪里。
    var postionLocation = gl.getAttribLocation(program,'a_position')
    var texCoordLocation = gl.getAttribLocation(program,'a_texCoord')
		var matrixLocation = gl.getUniformLocation(program, "u_matrix");
    // 片元着色器 卷积属性
    var kernelLocation = gl.getUniformLocation(program, "u_kernel[0]");
    var kernelWeightLocation = gl.getUniformLocation(program, "u_kernelWeight");
    var u_filter = gl.getUniformLocation(program, "u_filter");

    var positionBuffer = gl.createBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER,positionBuffer)
    // 设置缓冲区数据
    setRectangle(gl,0,0,image.width*scale,image.height*scale)

    // 提供矩形的纹理坐标。
    var texCoordBuffer = gl.createBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER,texCoordBuffer)
    gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([
        0.0, 0.0,
        1.0, 0.0,
        0.0, 1.0,
        0.0, 1.0,
        1.0, 0.0,
        1.0, 1.0,
      ]),
      gl.STATIC_DRAW
    )

    // 创建纹理
    var texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D,texture)

    // 设置参数，以便我们可以渲染任何大小的图像。
    gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE)
    gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE)
    gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR)
    gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR)
    
    gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE, image)

    // 获取索引位置
    var resolutionLocation = gl.getUniformLocation(program,"u_resolution")
    var rgbaLocation = gl.getUniformLocation(program,'u_rgba')
    var textureSizeLocation = gl.getUniformLocation(program, "u_textureSize");
    var lightLocation = gl.getUniformLocation(program, "u_light");
    var saturationLocation = gl.getUniformLocation(program, "u_saturation");
    var contrastRatioLocation = gl.getUniformLocation(program, "u_contrastRatio");

    webglUtils.resizeCanvasToDisplaySize(gl.canvas)

    gl.viewport(0,0,gl.canvas.width,gl.canvas.height)

    gl.clearColor(0,0,0,0.5)
    gl.clear(gl.COLOR_BUFFER_BIT)

    gl.useProgram(program)

    gl.enableVertexAttribArray(positionBuffer)

    gl.bindBuffer(gl.ARRAY_BUFFER,positionBuffer)

    var size = 2;
    var type = gl.FLOAT;
    var normalize = false;
    var stride = 0;
    var offset = 0;
    gl.vertexAttribPointer(positionBuffer,size,type,normalize,stride,offset);

    gl.enableVertexAttribArray(texCoordLocation)
    gl.bindBuffer(gl.ARRAY_BUFFER,texCoordBuffer)

    var size = 2 ;
    var type = gl.FLOAT;
    gl.vertexAttribPointer(texCoordLocation,size,type,normalize,stride,offset)

    // 设置全局属性
    gl.uniform2f(resolutionLocation,gl.canvas.width,gl.canvas.height);
    gl.uniform4fv(rgbaLocation,rgba);
    gl.uniform1f(lightLocation,light);
    gl.uniform1f(saturationLocation,saturation);
    gl.uniform1f(contrastRatioLocation,contrastRatio);

    // 卷积计算属性
    if(filterName){
      gl.uniform1i(u_filter, 1);
    }else{
      gl.uniform1i(u_filter, -1);
    }
    gl.uniform1fv(kernelLocation, kernels[filterName]);
    gl.uniform1f(kernelWeightLocation, computeKernelWeight(kernels[filterName]));
    // 设置图像的大小
    gl.uniform2f(textureSizeLocation, image.width*scale, image.height*scale);

    {
      // 平移旋转中心点，旋转
      var matrix = getMmatrix(gl.canvas.clientWidth,gl.canvas.clientHeight,[-image.width*scale/2,-image.height*scale/2])
      // 矩阵运算
      gl.uniformMatrix3fv(matrixLocation, false, matrix);
    }
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = 6;
    // 绘制
    gl.drawArrays(primitiveType,offset,count);
  }

  function setRectangle(gl,x,y,width,height){
    var x1 = x;
    var x2 = x+width;
    var y1 = y;
    var y2 = y+height;
    gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([
        x1,y1,
        x2,y1,
        x1,y2,
        x1,y2,
        x2,y1,
        x2,y2
      ]),
      gl.STATIC_DRAW
    )
  }
</script>
</html>